/*******************************************************************************
 * Copyright (c) 2009 Luaj.org. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/
package org.luaj.vm2.compiler;

//TODO 与3.0不一样

import org.luaj.vm2.LocVars;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.FuncState.BlockCnt;
import org.luaj.vm2.lib.MathLib;

import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;


public class LexState {

    protected static String RESERVED_LOCAL_VAR_FOR_CONTROL = "(for control)";
    protected static String RESERVED_LOCAL_VAR_FOR_STATE = "(for state)";
    protected static String RESERVED_LOCAL_VAR_FOR_GENERATOR = "(for generator)";
    protected static String RESERVED_LOCAL_VAR_FOR_STEP = "(for step)";
    protected static String RESERVED_LOCAL_VAR_FOR_LIMIT = "(for limit)";
    protected static String RESERVED_LOCAL_VAR_FOR_INDEX = "(for index)";

    // keywords array
    protected static String[] RESERVED_LOCAL_VAR_KEYWORDS = new String[]{
            RESERVED_LOCAL_VAR_FOR_CONTROL,
            RESERVED_LOCAL_VAR_FOR_GENERATOR,
            RESERVED_LOCAL_VAR_FOR_INDEX,
            RESERVED_LOCAL_VAR_FOR_LIMIT,
            RESERVED_LOCAL_VAR_FOR_STATE,
            RESERVED_LOCAL_VAR_FOR_STEP
    };
    static Hashtable RESERVED_LOCAL_VAR_KEYWORDS_TABLE = new Hashtable();

    static {
        for (int i = 0; i < RESERVED_LOCAL_VAR_KEYWORDS.length; i++)
            RESERVED_LOCAL_VAR_KEYWORDS_TABLE.put(RESERVED_LOCAL_VAR_KEYWORDS[i], Boolean.TRUE);
    }

    static final int EOZ = (-1);
    final int MAX_INT = Integer.MAX_VALUE - 2;
    static final int UCHAR_MAX = 255; // TODO, convert to unicode CHAR_MAX?
    static final int LUAI_MAXCCALLS = 200;

    static String LUA_QS(String s) {
        return "'" + s + "'";
    }

    static String LUA_QL(Object o) {
        return LUA_QS(String.valueOf(o));
    }

    static int LUA_COMPAT_LSTR = 1; // 1 for compatibility, 2 for old behavior
//	 static  boolean LUA_COMPAT_VARARG = true;

    public static boolean isReservedKeyword(String varName) {
        return RESERVED_LOCAL_VAR_KEYWORDS_TABLE.containsKey(varName);
    }

    /*
    ** Marks the end of a patch list. It is an invalid value both as an absolute
    ** address, and as a list link (would link an element to itself).
    */
    static int NO_JUMP = (-1);

    /*
    ** grep "ORDER OPR" if you change these enums
    */
    static final int
            OPR_ADD = 0, OPR_SUB = 1, OPR_MUL = 2, OPR_DIV = 3, OPR_MOD = 4, OPR_POW = 5,
            OPR_CONCAT = 6,
            OPR_NE = 7, OPR_EQ = 8,
            OPR_LT = 9, OPR_LE = 10, OPR_GT = 11, OPR_GE = 12,
            OPR_AND = 13, OPR_OR = 14,
            OPR_NOBINOPR = 15;

    static final int
            OPR_MINUS = 0, OPR_NOT = 1, OPR_LEN = 2, OPR_NOUNOPR = 3;

    /* exp kind */
    static final int
            VVOID = 0,	/* no value */
            VNIL = 1,
            VTRUE = 2,
            VFALSE = 3,
            VK = 4,		/* info = index of constant in `k' */
            VKNUM = 5,	/* nval = numerical value */
            VNONRELOC = 6,	/* info = result register */
            VLOCAL = 7,	/* info = local register */
            VUPVAL = 8,       /* info = index of upvalue in `upvalues' */
            VINDEXED = 9,	/* info = table register, aux = index register (or `k') */
            VJMP = 10,		/* info = instruction pc */
            VRELOCABLE = 11,	/* info = instruction pc */
            VCALL = 12,	/* info = instruction pc */
            VVARARG = 13;	/* info = instruction pc */

    /* semantics information */
    static class SemInfo {
        LuaValue r;
        LuaString ts;
    }


    static class Token {
        int token;
        SemInfo seminfo = new SemInfo();

        public void set(Token other) {
            this.token = other.token;
            this.seminfo.r = other.seminfo.r;
            this.seminfo.ts = other.seminfo.ts;
        }
    }


    int current;  /* current character (charint) */
    int linenumber;  /* input line counter */
    int lastline;  /* line of last token `consumed' */
    Token t = new Token();  /* current token */
    Token lookahead = new Token();  /* look ahead token */
    Token lookahead2 = new Token();  /* look ahead token */
    FuncState fs;  /* `FuncState' is  to the parser */
    LuaC L;
    InputStream z;  /* input stream */
    char[] buff;  /* buffer for tokens */
    int nbuff; /* length of buffer */
    Dyndata dyd = new Dyndata();  /* dynamic structures used by the parser */
    LuaString source;  /* current source name */
    LuaString envn;  /* environment variable name */
    byte decpoint;  /* locale decimal point */

    /* ORDER RESERVED */
    static String luaX_tokens[] = {
            "and", "break", "do", "else", "elseif",
            "end", "false", "for", "function", "goto", "if",
            "in", "local", "nil", "not", "or", "repeat",
            "return", "then", "true", "until", "while",
            "..", "...", "==", ">=", "<=", "~=",
            "::", "<eos>", "<number>", "<name>", "<string>", "<eof>",
    };

    static final int
        /* terminal symbols denoted by reserved words */
            TK_AND = 257, TK_BREAK = 258, TK_DO = 259, TK_ELSE = 260, TK_ELSEIF = 261,
            TK_END = 262, TK_FALSE = 263, TK_FOR = 264, TK_FUNCTION = 265, TK_GOTO = 266, TK_IF = 267,
            TK_IN = 268, TK_LOCAL = 269, TK_NIL = 270, TK_NOT = 271, TK_OR = 272, TK_REPEAT = 273,
            TK_RETURN = 274, TK_THEN = 275, TK_TRUE = 276, TK_UNTIL = 277, TK_WHILE = 278,
    /* other terminal symbols */
    TK_CONCAT = 279, TK_DOTS = 280, TK_EQ = 281, TK_GE = 282, TK_LE = 283, TK_NE = 284,
            TK_DBCOLON = 285, TK_EOS = 286, TK_NUMBER = 287, TK_NAME = 288, TK_STRING = 289;

    static int FIRST_RESERVED = TK_AND;
    static int NUM_RESERVED = TK_WHILE + 1 - FIRST_RESERVED;

    static Hashtable RESERVED = new Hashtable();

    static {
        for (int i = 0; i < NUM_RESERVED; i++) {
            LuaString ts = (LuaString) LuaValue.valueOf(luaX_tokens[i]);
            RESERVED.put(ts, new Integer(FIRST_RESERVED + i));
        }
    }

    boolean isalnum(int c) {
        return (c >= '0' && c <= '9')
                || (c >= 'a' && c <= 'z')
                || (c >= 'A' && c <= 'Z')
                || (c == '_');
        // return Character.isLetterOrDigit(c);
    }

    boolean isalpha(int c) {
        return (c >= 'a' && c <= 'z')
                || (c >= 'A' && c <= 'Z');
    }

    boolean isdigit(int c) {
        return (c >= '0' && c <= '9');
    }

    boolean isxdigit(int c) {
        return (c >= '0' && c <= '9')
                || (c >= 'a' && c <= 'f')
                || (c >= 'A' && c <= 'F');
    }

    boolean isspace(int c) {
        return (c <= ' ');
    }

    //是否标准语法
    boolean standardSyntax = true;

    public LexState(LuaC state, InputStream stream, boolean standardSyntax) {
        this.z = stream;
        this.buff = new char[32];
        this.L = state;
        this.standardSyntax = standardSyntax;
    }

    void nextChar() {
        try {
            current = z.read();
        } catch (IOException e) {
            e.printStackTrace();
            current = EOZ;
        }
    }

    boolean currIsNewline() {
        return current == '\n' || current == '\r';
    }

    void save_and_next() {
        save(current);
        nextChar();
    }

    void save(int c) {
        if (buff == null || nbuff + 1 > buff.length)
            buff = LuaC.realloc(buff, nbuff * 2 + 1);
        buff[nbuff++] = (char) c;
    }


    String token2str(int token) {
        if (token < FIRST_RESERVED) {
            return iscntrl(token) ?
                    L.pushfstring("char(" + ((int) token) + ")") :
                    L.pushfstring(String.valueOf((char) token));
        } else {
            return luaX_tokens[token - FIRST_RESERVED];
        }
    }

    static boolean iscntrl(int token) {
        return token < ' ';
    }

    String txtToken(int token) {
        switch (token) {
            case TK_NAME:
            case TK_STRING:
            case TK_NUMBER:
                return new String(buff, 0, nbuff);
            default:
                return token2str(token);
        }
    }

    void lexerror(String msg, int token) {
        String cid = Lua.chunkid(source.tojstring());
        L.pushfstring(cid + ":" + linenumber + ": " + msg);
        if (token != 0)
            L.pushfstring("syntax error: " + msg + " near " + txtToken(token));
//		throw new LuaError(cid+":"+linenumber+": "+msg);
        throw new LuaError(cid + ":" + linenumber + ": " + msg + " near " + txtToken(token));//modified by song
    }

    void syntaxerror(String msg) {
        lexerror(msg, t.token);
    }

    // only called by new_localvarliteral() for var names.
    LuaString newstring(String s) {
        return L.newTString(s);
    }

    LuaString newstring(char[] chars, int offset, int len) {
        return L.newTString(new String(chars, offset, len));
    }

    void inclinenumber() {
        int old = current;
        LuaC._assert(currIsNewline());
        nextChar(); /* skip '\n' or '\r' */
        if (currIsNewline() && current != old)
            nextChar(); /* skip '\n\r' or '\r\n' */
        if (++linenumber >= MAX_INT)
            syntaxerror("chunk has too many lines");
    }

    void setinput(LuaC L, int firstByte, InputStream z, LuaString source) {
        this.decpoint = '.';
        this.L = L;
        this.lookahead.token = TK_EOS; /* no look-ahead token */
        this.lookahead2.token = TK_EOS;
        this.z = z;
        this.fs = null;
        this.linenumber = 1;
        this.lastline = 1;
        this.source = source;
        this.envn = LuaValue.ENV;  /* environment variable name */
        this.nbuff = 0;   /* initialize buffer */
        this.current = firstByte; /* read first char */
        this.skipShebang();
    }

    void skipShebang() {
        if (current == '#')
            while (!currIsNewline() && current != EOZ)
                nextChar();
    }



	/*
    ** =======================================================
	** LEXICAL ANALYZER
	** =======================================================
	*/


    boolean check_next(String set) {
        if (set.indexOf(current) < 0)
            return false;
        save_and_next();
        return true;
    }
//remove by yanqiu
//    void buffreplace(char from, char to) {
//        int n = nbuff;
//        char[] p = buff;
//        while ((--n) >= 0)
//            if (p[n] == from)
//                p[n] = to;
//    }

    LuaValue strx2number(String str, SemInfo seminfo) {
        char[] c = str.toCharArray();
        int s = 0;
        while (s < c.length && isspace(c[s]))
            ++s;
        // Check for negative sign
        double sgn = 1.0;
        if (s < c.length && c[s] == '-') {
            sgn = -1.0;
            ++s;
        }
        /* Check for "0x" */
        if (s + 2 >= c.length)
            return LuaValue.ZERO;
        if (c[s++] != '0')
            return LuaValue.ZERO;
        if (c[s] != 'x' && c[s] != 'X')
            return LuaValue.ZERO;
        ++s;

        // read integer part.
        double m = 0;
        int e = 0;
        while (s < c.length && isxdigit(c[s]))
            m = (m * 16) + hexvalue(c[s++]);
        if (s < c.length && c[s] == '.') {
            ++s;  // skip dot
            while (s < c.length && isxdigit(c[s])) {
                m = (m * 16) + hexvalue(c[s++]);
                e -= 4;  // Each fractional part shifts right by 2^4
            }
        }
        if (s < c.length && (c[s] == 'p' || c[s] == 'P')) {
            ++s;
            int exp1 = 0;
            boolean neg1 = false;
            if (s < c.length && c[s] == '-') {
                neg1 = true;
                ++s;
            }
            while (s < c.length && isdigit(c[s]))
                exp1 = exp1 * 10 + c[s++] - '0';
            if (neg1)
                exp1 = -exp1;
            e += exp1;
        }
        return LuaValue.valueOf(sgn * m * MathLib.dpow_d(2.0, e));
    }

    boolean str2d(String str, SemInfo seminfo) {
        if (str.indexOf('n') >= 0 || str.indexOf('N') >= 0)
            seminfo.r = LuaValue.ZERO;
        else if (str.indexOf('x') >= 0 || str.indexOf('X') >= 0)
            seminfo.r = strx2number(str, seminfo);
        else
            seminfo.r = LuaValue.valueOf(Double.parseDouble(str.trim()));
        return true;
    }

    void read_numeral(SemInfo seminfo) {
        String expo = "Ee";
        int first = current;
        LuaC._assert(isdigit(current));
        save_and_next();
        if (first == '0' && check_next("Xx"))
            expo = "Pp";
        while (true) {
            if (check_next(expo))
                check_next("+-");
            if (isxdigit(current) || current == '.')
                save_and_next();
            else
                break;
        }
        save('\0');
        String str = new String(buff, 0, nbuff);
        str2d(str, seminfo);
    }

    int skip_sep() {
        int count = 0;
        int s = current;
        LuaC._assert(s == '[' || s == ']');
        save_and_next();
        while (current == '=') {
            save_and_next();
            count++;
        }
        return (current == s) ? count : (-count) - 1;
    }

    void read_long_string(SemInfo seminfo, int sep) {
        int cont = 0;
        save_and_next(); /* skip 2nd `[' */
        if (currIsNewline()) /* string starts with a newline? */
            inclinenumber(); /* skip it */
        for (boolean endloop = false; !endloop; ) {
            switch (current) {
                case EOZ:
                    lexerror((seminfo != null) ? "unfinished long string"
                            : "unfinished long comment", TK_EOS);
                    break; /* to avoid warnings */
                case '[': {
                    if (skip_sep() == sep) {
                        save_and_next(); /* skip 2nd `[' */
                        cont++;
                        if (LUA_COMPAT_LSTR == 1) {
                            if (sep == 0)
                                lexerror("nesting of [[...]] is deprecated", '[');
                        }
                    }
                    break;
                }
                case ']': {
                    if (skip_sep() == sep) {
                        save_and_next(); /* skip 2nd `]' */
                        if (LUA_COMPAT_LSTR == 2) {
                            cont--;
                            if (sep == 0 && cont >= 0)
                                break;
                        }
                        endloop = true;
                    }
                    break;
                }
                case '\n':
                case '\r': {
                    save('\n');
                    inclinenumber();
                    if (seminfo == null)
                        nbuff = 0; /* avoid wasting space */
                    break;
                }
                default: {
                    if (seminfo != null)
                        save_and_next();
                    else
                        nextChar();
                }
            }
        }
        if (seminfo != null)
            seminfo.ts = L.newTString(LuaString.valueOf(buff, 2 + sep, nbuff - 2 * (2 + sep)));
    }

    int hexvalue(int c) {
        return c <= '9' ? c - '0' : c <= 'F' ? c + 10 - 'A' : c + 10 - 'a';
    }

    int readhexaesc() {
        nextChar();
        int c1 = current;
        nextChar();
        int c2 = current;
        if (!isxdigit(c1) || !isxdigit(c2))
            lexerror("hexadecimal digit expected 'x" + ((char) c1) + ((char) c2), TK_STRING);
        return (hexvalue(c1) << 4) + hexvalue(c2);
    }

    void read_string(int del, SemInfo seminfo) {
        save_and_next();
        while (current != del) {
            switch (current) {
                case EOZ:
                    lexerror("unfinished string", TK_EOS);
                    continue; /* to avoid warnings */
                case '\n':
                case '\r':
                    lexerror("unfinished string", TK_STRING);
                    continue; /* to avoid warnings */
                case '\\': {
                    int c;
                    nextChar(); /* do not save the `\' */
                    switch (current) {
                        case 'a': /* bell */
                            c = '\u0007';
                            break;
                        case 'b': /* backspace */
                            c = '\b';
                            break;
                        case 'f': /* form feed */
                            c = '\f';
                            break;
                        case 'n': /* newline */
                            c = '\n';
                            break;
                        case 'r': /* carriage return */
                            c = '\r';
                            break;
                        case 't': /* tab */
                            c = '\t';
                            break;
                        case 'v': /* vertical tab */
                            c = '\u000B';
                            break;
                        case 'x':
                            c = readhexaesc();
                            break;
                        case '\n': /* go through */
                        case '\r':
                            save('\n');
                            inclinenumber();
                            continue;
                        case EOZ:
                            continue; /* will raise an error next loop */
                        case 'z': {  /* zap following span of spaces */
                            nextChar();  /* skip the 'z' */
                            while (isspace(current)) {
                                if (currIsNewline()) inclinenumber();
                                else nextChar();
                            }
                            continue;
                        }
                        default: {
                            if (!isdigit(current))
                                save_and_next(); /* handles \\, \", \', and \? */
                            else { /* \xxx */
                                int i = 0;
                                c = 0;
                                do {
                                    c = 10 * c + (current - '0');
                                    nextChar();
                                } while (++i < 3 && isdigit(current));
                                if (c > UCHAR_MAX)
                                    lexerror("escape sequence too large", TK_STRING);
                                save(c);
                            }
                            continue;
                        }
                    }
                    save(c);
                    nextChar();
                    continue;
                }
                default:
                    save_and_next();
            }
        }
        save_and_next(); /* skip delimiter */
        seminfo.ts = L.newTString(LuaString.valueOf(buff, 1, nbuff - 2));
    }

    int llex(SemInfo seminfo) {
        nbuff = 0;
        while (true) {
            switch (current) {
                case '\n':
                case '\r': {
                    inclinenumber();
                    continue;
                }
                case '-': {
                    nextChar();
                    if (current != '-')
                        return '-';
                /* else is a comment */
                    nextChar();
                    if (current == '[') {
                        int sep = skip_sep();
                        nbuff = 0; /* `skip_sep' may dirty the buffer */
                        if (sep >= 0) {
                            read_long_string(null, sep); /* long comment */
                            nbuff = 0;
                            continue;
                        }
                    }
				/* else short comment */
                    while (!currIsNewline() && current != EOZ)
                        nextChar();
                    continue;
                }
                case '[': {
                    int sep = skip_sep();
                    if (sep >= 0) {
                        read_long_string(seminfo, sep);
                        return TK_STRING;
                    } else if (sep == -1)
                        return '[';
                    else
                        lexerror("invalid long string delimiter", TK_STRING);
                }
                case '=': {
                    nextChar();
                    if (current != '=')
                        return '=';
                    else {
                        nextChar();
                        return TK_EQ;
                    }
                }
                case '<': {
                    nextChar();
                    if (current != '=')
                        return '<';
                    else {
                        nextChar();
                        return TK_LE;
                    }
                }
                case '>': {
                    nextChar();
                    if (current != '=')
                        return '>';
                    else {
                        nextChar();
                        return TK_GE;
                    }
                }
                case '~': {
                    nextChar();
                    if (current != '=')
                        return '~';
                    else {
                        nextChar();
                        return TK_NE;
                    }
                }
                case ':': {
                    nextChar();
                    if (current != ':')
                        return ':';
                    else {
                        nextChar();
                        return TK_DBCOLON;
                    }
                }
                case '"':
                case '\'': {
                    read_string(current, seminfo);
                    return TK_STRING;
                }
                case '.': {
                    save_and_next();
                    if (check_next(".")) {
                        if (check_next("."))
                            return TK_DOTS; /* ... */
                        else
                            return TK_CONCAT; /* .. */
                    } else if (!isdigit(current))
                        return '.';
                    else {
                        read_numeral(seminfo);
                        return TK_NUMBER;
                    }
                }
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9': {
                    read_numeral(seminfo);
                    return TK_NUMBER;
                }
                case EOZ: {
                    return TK_EOS;
                }
                default: {
                    if (isspace(current)) {
                        LuaC._assert(!currIsNewline());
                        nextChar();
                        continue;
                    } else if (isdigit(current)) {
                        read_numeral(seminfo);
                        return TK_NUMBER;
                    } else if (isalpha(current) || current == '_') {
					/* identifier or reserved word */
                        LuaString ts;
                        do {
                            save_and_next();
                        } while (isalnum(current) || current == '_');
                        ts = newstring(buff, 0, nbuff);
                        if (RESERVED.containsKey(ts))
                            return ((Integer) RESERVED.get(ts)).intValue();
                        else {
                            seminfo.ts = ts;
                            return TK_NAME;
                        }
                    } else {
                        int c = current;
                        nextChar();
                        return c; /* single-char tokens (+ - / ...) */
                    }
                }
            }
        }
    }

    void next() {
        lastline = linenumber;
        if (lookahead.token != TK_EOS) { /* is there a look-ahead token? */
            t.set(lookahead); /* use this one */
            lookahead.token = TK_EOS; /* and discharge it */

            lookahead.set(lookahead2);
            lookahead2.token = TK_EOS;
        } else {
            t.token = llex(t.seminfo); /* read next token */
        }
    }

    void lookahead() {
        if (lookahead.token == TK_EOS) {
            lookahead.token = llex(lookahead.seminfo);
        }
        if (lookahead2.token == TK_EOS) {
            lookahead2.token = llex(lookahead2.seminfo);
        }
    }

    // =============================================================
    // from lcode.h
    // =============================================================


    // =============================================================
    // from lparser.c
    // =============================================================

//    static boolean vkisvar(int k) {
//        return (VLOCAL <= (k) && (k) <= VINDEXED);
//    } remove by yanqiu
//
//    static boolean vkisinreg(int k) {
//        return ((k) == VNONRELOC || (k) == VLOCAL);
//    } remove by yanqiu

    static class expdesc {
        int k; // expkind, from enumerated list, above

        static class U { // originally a union
            short ind_idx; // index (R/K)
            short ind_t; // table(register or upvalue)
            short ind_vt; // whether 't' is register (VLOCAL) or (UPVALUE)
            LuaValue _nval;
            int info;

            public void setNval(LuaValue r) {
                _nval = r;
            }

            public LuaValue nval() {
                return (_nval == null ? LuaInteger.valueOf(info) : _nval);
            }
        }

        ;
        U u = new U();
        IntPtr t = new IntPtr(); /* patch list of `exit when true' */
        IntPtr f = new IntPtr(); /* patch list of `exit when false' */

        void init(int k, int i) {
            this.f.i = NO_JUMP;
            this.t.i = NO_JUMP;
            this.k = k;
            this.u.info = i;
        }

        boolean hasjumps() {
            return (t.i != f.i);
        }

        boolean isnumeral() {
            return (k == VKNUM && t.i == NO_JUMP && f.i == NO_JUMP);
        }

        public void setvalue(expdesc other) {
            this.f.i = other.f.i;
            this.k = other.k;
            this.t.i = other.t.i;
            this.u._nval = other.u._nval;
            this.u.ind_idx = other.u.ind_idx;
            this.u.ind_t = other.u.ind_t;
            this.u.ind_vt = other.u.ind_vt;
            this.u.info = other.u.info;
        }
    }


    /* description of active local variable */
    static class Vardesc {
        short idx;  /* variable index in stack */

        Vardesc(int idx) {
            this.idx = (short) idx;
        }
    }



    /* description of pending goto statements and label statements */
    static class Labeldesc {
        LuaString name;  /* label identifier */
        int pc;  /* position in code */
        int line;  /* line where it appeared */
        short nactvar;  /* local level where it appears in current block */

        public Labeldesc(LuaString name, int pc, int line, short nactvar) {
            this.name = name;
            this.pc = pc;
            this.line = line;
            this.nactvar = nactvar;
        }
    }



    /* dynamic structures used by the parser */
    static class Dyndata {
        Vardesc[] actvar;  /* list of active local variables */
        int n_actvar = 0;
        Labeldesc[] gt;  /* list of pending gotos */
        int n_gt = 0;
        Labeldesc[] label;   /* list of active labels */
        int n_label = 0;
    }



    boolean hasmultret(int k) {
        return ((k) == VCALL || (k) == VVARARG);
    }

	/*----------------------------------------------------------------------
	name		args	description
	------------------------------------------------------------------------*/

//    void anchor_token() {
//		/* last token from outer function must be EOS */
//        LuaC._assert(fs != null || t.token == TK_EOS);
//        if (t.token == TK_NAME || t.token == TK_STRING) {
//            LuaString ts = t.seminfo.ts;
//            // TODO: is this necessary?
//            L.cachedLuaString(t.seminfo.ts);
//        }
//    }remove by yanqiu

    /* semantic error */
    void semerror(String msg) {
        t.token = 0;  /* remove 'near to' from  message */
        syntaxerror(msg);
    }

    void error_expected(int token) {
        syntaxerror(L.pushfstring(LUA_QS(token2str(token)) + " expected"));
    }

    boolean testnext(int c) {
        if (t.token == c) {
            next();
            return true;
        } else
            return false;
    }

    void check(int c) {
        if (t.token != c)
            error_expected(c);
    }

    void checknext(int c) {
        check(c);
        next();
    }

    void check_condition(boolean c, String msg) {
        if (!(c))
            syntaxerror(msg);
    }


    void check_match(int what, int who, int where) {
        if (!testnext(what)) {
            if (where == linenumber)
                error_expected(what);
            else {
                syntaxerror(L.pushfstring(LUA_QS(token2str(what))
                        + " expected " + "(to close " + LUA_QS(token2str(who))
                        + " at line " + where + ")"));
            }
        }
    }

    LuaString str_checkname() {
        LuaString ts;
        check(TK_NAME);
        ts = t.seminfo.ts;
        next();
        return ts;
    }

    void codestring(expdesc e, LuaString s) {
        e.init(VK, fs.stringK(s));
    }

    void checkname(expdesc e) {
        codestring(e, str_checkname());
    }


    int registerlocalvar(LuaString varname) {
        FuncState fs = this.fs;
        Prototype f = fs.f;
        if (f.locvars == null || fs.nlocvars + 1 > f.locvars.length)
            f.locvars = LuaC.realloc(f.locvars, fs.nlocvars * 2 + 1);
        f.locvars[fs.nlocvars] = new LocVars(varname, 0, 0);
        return fs.nlocvars++;
    }

    void new_localvar(LuaString name) {
        int reg = registerlocalvar(name);
        fs.checklimit(dyd.n_actvar + 1, FuncState.LUAI_MAXVARS, "local variables");
        if (dyd.actvar == null || dyd.n_actvar + 1 > dyd.actvar.length)
            dyd.actvar = LuaC.realloc(dyd.actvar, Math.max(1, dyd.n_actvar * 2));
        dyd.actvar[dyd.n_actvar++] = new Vardesc(reg);
    }

    void new_localvarliteral(String v) {
        LuaString ts = newstring(v);
        new_localvar(ts);
    }

    void adjustlocalvars(int nvars) {
        FuncState fs = this.fs;
        fs.nactvar = (short) (fs.nactvar + nvars);
        for (; nvars > 0; nvars--) {
            fs.getlocvar(fs.nactvar - nvars).startpc = fs.pc;
        }
    }
//
//    void removevars(int tolevel) {
//        FuncState fs = this.fs;
//        while (fs.nactvar > tolevel)
//            fs.getlocvar(--fs.nactvar).endpc = fs.pc;
//    }remove by yanqiu

    void singlevar(expdesc var) {
        LuaString varname = this.str_checkname();
        FuncState fs = this.fs;
        if (FuncState.singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */
            expdesc key = new expdesc();
            FuncState.singlevaraux(fs, this.envn, var, 1);  /* get environment variable */
            LuaC._assert(var.k == VLOCAL || var.k == VUPVAL);
            this.codestring(key, varname);  /* key is variable name */
            fs.indexed(var, key);  /* env[varname] */
        }
    }

    void adjust_assign(int nvars, int nexps, expdesc e) {
        FuncState fs = this.fs;
        int extra = nvars - nexps;
        if (hasmultret(e.k)) {
			/* includes call itself */
            extra++;
            if (extra < 0)
                extra = 0;
			/* last exp. provides the difference */
            fs.setreturns(e, extra);
            if (extra > 1)
                fs.reserveregs(extra - 1);
        } else {
			/* close last expression */
            if (e.k != VVOID)
                fs.exp2nextreg(e);
            if (extra > 0) {
                int reg = fs.freereg;
                fs.reserveregs(extra);
                fs.nil(reg, extra);
            }
        }
    }

    void enterlevel() {
        if (++L.nCcalls > LUAI_MAXCCALLS)
            lexerror("chunk has too many syntax levels", 0);
    }

    void leavelevel() {
        L.nCcalls--;
    }

    void closegoto(int g, Labeldesc label) {
        FuncState fs = this.fs;
        Labeldesc[] gl = this.dyd.gt;
        Labeldesc gt = gl[g];
        LuaC._assert(gt.name.eq_b(label.name));
        if (gt.nactvar < label.nactvar) {
            LuaString vname = fs.getlocvar(gt.nactvar).varname;
            String msg = L.pushfstring("<goto " + gt.name + "> at line "
                    + gt.line + " jumps into the scope of local '"
                    + vname.tojstring() + "'");
            semerror(msg);
        }
        fs.patchlist(gt.pc, label.pc);
		/* remove goto from pending list */
        System.arraycopy(gl, g + 1, gl, g, this.dyd.n_gt - g - 1);
        gl[--this.dyd.n_gt] = null;
    }

    /*
     ** try to close a goto with existing labels; this solves backward jumps
     */
    boolean findlabel(int g) {
        int i;
        BlockCnt bl = fs.bl;
        Dyndata dyd = this.dyd;
        Labeldesc gt = dyd.gt[g];
		/* check labels in current block for a match */
        for (i = bl.firstlabel; i < dyd.n_label; i++) {
            Labeldesc lb = dyd.label[i];
            if (lb.name.eq_b(gt.name)) {  /* correct label? */
                if (gt.nactvar > lb.nactvar &&
                        (bl.upval || dyd.n_label > bl.firstlabel))
                    fs.patchclose(gt.pc, lb.nactvar);
                closegoto(g, lb);  /* close it */
                return true;
            }
        }
        return false;  /* label not found; cannot close goto */
    }

    /* Caller must LuaC.grow() the vector before calling this. */
    int newlabelentry(Labeldesc[] l, int index, LuaString name, int line, int pc) {
        l[index] = new Labeldesc(name, pc, line, fs.nactvar);
        return index;
    }

    /*
     ** check whether new label 'lb' matches any pending gotos in current
     ** block; solves forward jumps
     */
    void findgotos(Labeldesc lb) {
        Labeldesc[] gl = dyd.gt;
        int i = fs.bl.firstgoto;
        while (i < dyd.n_gt) {
            if (gl[i].name.eq_b(lb.name))
                closegoto(i, lb);
            else
                i++;
        }
    }


    /*
    ** create a label named "break" to resolve break statements
    */
    void breaklabel() {
        LuaString n = LuaString.valueOf("break");
        int l = newlabelentry(dyd.label = LuaC.grow(dyd.label, dyd.n_label + 1), dyd.n_label++, n, 0, fs.pc);
        findgotos(dyd.label[l]);
    }

    /*
    ** generates an error for an undefined 'goto'; choose appropriate
    ** message when label name is a reserved word (which can only be 'break')
    */
    void undefgoto(Labeldesc gt) {
        String msg = L.pushfstring(isReservedKeyword(gt.name.tojstring())
                ? "<" + gt.name + "> at line " + gt.line + " not inside a loop"
                : "no visible label '" + gt.name + "' for <goto> at line " + gt.line);
        semerror(msg);
    }

    Prototype addprototype() {
        Prototype clp;
        Prototype f = fs.f;  /* prototype of current function */
        if (f.p == null || fs.np >= f.p.length) {
            f.p = LuaC.realloc(f.p, Math.max(1, fs.np * 2));
        }
        f.p[fs.np++] = clp = new Prototype();
        return clp;
    }

    void codeclosure(expdesc v) {
        FuncState fs = this.fs.prev;
        v.init(VRELOCABLE, fs.codeABx(LuaC.OP_CLOSURE, 0, fs.np - 1));
        fs.exp2nextreg(v);  /* fix it at stack top (for GC) */
    }

    void open_func(FuncState fs, BlockCnt bl) {
        fs.prev = this.fs;  /* linked list of funcstates */
        fs.ls = this;
        this.fs = fs;
        fs.pc = 0;
        fs.lasttarget = -1;
        fs.jpc = new IntPtr(NO_JUMP);
        fs.freereg = 0;
        fs.nk = 0;
        fs.np = 0;
        fs.nups = 0;
        fs.nlocvars = 0;
        fs.nactvar = 0;
        fs.firstlocal = dyd.n_actvar;
        fs.bl = null;
        fs.f.source = this.source;
        fs.f.maxstacksize = 2;  /* registers 0/1 are always valid */
        fs.enterblock(bl, false);
    }

    void close_func() {
        FuncState fs = this.fs;
        Prototype f = fs.f;
        fs.ret(0, 0); /*  return */
        fs.leaveblock();
        f.code = LuaC.realloc(f.code, fs.pc);
        f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc);
        f.k = LuaC.realloc(f.k, fs.nk);
        f.p = LuaC.realloc(f.p, fs.np);
        f.locvars = LuaC.realloc(f.locvars, fs.nlocvars);
        f.upvalues = LuaC.realloc(f.upvalues, fs.nups);
        LuaC._assert(fs.bl == null);
        this.fs = fs.prev;
        // last token read was anchored in defunct function; must reanchor it
        // ls.anchor_token();
    }

	/*============================================================*/
	/* GRAMMAR RULES */
	/*============================================================*/

    void fieldsel(expdesc v) {
		/* fieldsel -> ['.' | ':'] NAME */
        FuncState fs = this.fs;
        expdesc key = new expdesc();
        fs.exp2anyregup(v);
        this.next(); /* skip the dot or colon */
        this.checkname(key);
        fs.indexed(v, key);
    }

    void yindex(expdesc v) {
		/* index -> '[' expr ']' */
        this.next(); /* skip the '[' */
        this.expr(v);
        this.fs.exp2val(v);
        this.checknext(']');
    }


 /*
	** {======================================================================
	** Rules for Constructors
	** =======================================================================
	*/


    static class ConsControl {
        expdesc v = new expdesc(); /* last list item read */
        expdesc t; /* table descriptor */
        int nh; /* total number of `record' elements */
        int na; /* total number of array elements */
        int tostore; /* number of array elements pending to be stored */
    }

    ;


    void recfield(ConsControl cc) {
		/* recfield -> (NAME | `['exp1`]') = exp1 */
        FuncState fs = this.fs;
        int reg = this.fs.freereg;
        expdesc key = new expdesc();
        expdesc val = new expdesc();
        int rkkey;
        if (this.t.token == TK_NAME) {
            fs.checklimit(cc.nh, MAX_INT, "items in a constructor");
            this.checkname(key);
        } else
			/* this.t.token == '[' */
            this.yindex(key);
        cc.nh++;
        this.checknext('=');
        rkkey = fs.exp2RK(key);
        this.expr(val);
        fs.codeABC(Lua.OP_SETTABLE, cc.t.u.info, rkkey, fs.exp2RK(val));
        fs.freereg = (short) reg; /* free registers */
    }

    void listfield(ConsControl cc) {
        this.expr(cc.v);
        fs.checklimit(cc.na, MAX_INT, "items in a constructor");
        cc.na++;
        cc.tostore++;
    }


    void constructor(expdesc t) {
		/* constructor -> ?? */
        FuncState fs = this.fs;
        int line = this.linenumber;
        int pc = fs.codeABC(Lua.OP_NEWTABLE, 0, 0, 0);
        ConsControl cc = new ConsControl();
        cc.na = cc.nh = cc.tostore = 0;
        cc.t = t;
        t.init(VRELOCABLE, pc);
        cc.v.init(VVOID, 0); /* no value (yet) */
        fs.exp2nextreg(t); /* fix it at stack top (for gc) */
        this.checknext('{');
        do {
            LuaC._assert(cc.v.k == VVOID || cc.tostore > 0);
            if (this.t.token == '}')
                break;
            fs.closelistfield(cc);
            switch (this.t.token) {
                case TK_NAME: { /* may be listfields or recfields */
                    this.lookahead();
                    if (this.lookahead.token != '=') /* expression? */
                        this.listfield(cc);
                    else
                        this.recfield(cc);
                    break;
                }
                case '[': { /* constructor_item -> recfield */
                    this.recfield(cc);
                    break;
                }
                default: { /* constructor_part -> listfield */
                    this.listfield(cc);
                    break;
                }
            }
        } while (this.testnext(',') || this.testnext(';'));
        this.check_match('}', '{', line);
        fs.lastlistfield(cc);
        InstructionPtr i = new InstructionPtr(fs.f.code, pc);
        LuaC.SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */
        LuaC.SETARG_C(i, luaO_int2fb(cc.nh));  /* set initial table size */
    }

    /*
    ** converts an integer to a "floating point byte", represented as
    ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
    ** eeeee != 0 and (xxx) otherwise.
    */
    static int luaO_int2fb(int x) {
        int e = 0;  /* expoent */
        while (x >= 16) {
            x = (x + 1) >> 1;
            e++;
        }
        if (x < 8) return x;
        else return ((e + 1) << 3) | (((int) x) - 8);
    }


	/* }====================================================================== */

    void parlist() {
	  /* parlist -> [ param { `,' param } ] */
        FuncState fs = this.fs;
        Prototype f = fs.f;
        int nparams = 0;
        f.is_vararg = 0;
        if (this.t.token != ')') {  /* is `parlist' not empty? */
            do {
                switch (this.t.token) {
                    case TK_NAME: {  /* param . NAME */
                        this.new_localvar(this.str_checkname());
                        ++nparams;
                        break;
                    }
                    case TK_DOTS: {  /* param . `...' */
                        this.next();
                        f.is_vararg = 1;
                        break;
                    }
                    default:
                        this.syntaxerror("<name> or " + LUA_QL("...") + " expected");
                }
            } while ((f.is_vararg == 0) && this.testnext(','));
        }
        this.adjustlocalvars(nparams);
        f.numparams = fs.nactvar;
        fs.reserveregs(fs.nactvar);  /* reserve register for parameters */
    }


    void body(expdesc e, boolean needself, int line) {
		/* body -> `(' parlist `)' chunk END */
        FuncState new_fs = new FuncState();
        BlockCnt bl = new BlockCnt();
        new_fs.f = addprototype();
        new_fs.f.linedefined = line;
        open_func(new_fs, bl);
        this.checknext('(');
        if (needself) {
            new_localvarliteral("self");
            adjustlocalvars(1);
        }
        this.parlist();
        this.checknext(')');
        this.statlist();
        new_fs.f.lastlinedefined = this.linenumber;
        this.check_match(TK_END, TK_FUNCTION, line);
        this.codeclosure(e);
        this.close_func();
    }

    int explist(expdesc v) {
		/* explist1 -> expr { `,' expr } */
        int n = 1; /* at least one expression */
        this.expr(v);
        while (this.testnext(',')) {
            fs.exp2nextreg(v);
            this.expr(v);
            n++;
        }
        return n;
    }


    void funcargs(expdesc f, int line) {
        FuncState fs = this.fs;
        expdesc args = new expdesc();
        int base, nparams;
        switch (this.t.token) {
            case '(': { /* funcargs -> `(' [ explist1 ] `)' */
                this.next();
                if (this.t.token == ')') /* arg list is empty? */
                    args.k = VVOID;
                else {
                    this.explist(args);
                    fs.setmultret(args);
                }
                this.check_match(')', '(', line);
                break;
            }
            case '{': { /* funcargs -> constructor */
                this.constructor(args);
                break;
            }
            case TK_STRING: { /* funcargs -> STRING */
                this.codestring(args, this.t.seminfo.ts);
                this.next(); /* must use `seminfo' before `next' */
                break;
            }
            default: {
                this.syntaxerror("function arguments expected");
                return;
            }
        }
        LuaC._assert(f.k == VNONRELOC);
        base = f.u.info; /* base register for call */
        if (hasmultret(args.k))
            nparams = Lua.LUA_MULTRET; /* open call */
        else {
            if (args.k != VVOID)
                fs.exp2nextreg(args); /* close last argument */
            nparams = fs.freereg - (base + 1);
        }
        f.init(VCALL, fs.codeABC(Lua.OP_CALL, base, nparams + 1, 2));
        fs.fixline(line);
        fs.freereg = (short) (base + 1);  /* call remove function and arguments and leaves
							 * (unless changed) one result */
    }


	/*
	** {======================================================================
	** Expression parsing
	** =======================================================================
	*/

    void primaryexp(expdesc v) {
		/* primaryexp -> NAME | '(' expr ')' */
        switch (t.token) {
            case '(': {
                int line = linenumber;
                this.next();
                this.expr(v);
                this.check_match(')', '(', line);
                fs.dischargevars(v);
                return;
            }
            case TK_NAME: {
                singlevar(v);
                return;
            }
            default: {
                this.syntaxerror("unexpected symbol " + t.token + " (" + ((char) t.token) + ")");
                return;
            }
        }
    }


    void suffixedexp(expdesc v) {
		/* suffixedexp ->
       	primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
        int line = linenumber;
        primaryexp(v);
        for (; ; ) {
            switch (t.token) {
                case '.': { /* fieldsel */
                    if (standardSyntax) {//标准语法
                        this.fieldsel(v);
                    } else {
                        this.lookahead();
                        if (t.token == '.' && lookahead.token == TK_NAME && (lookahead2.token == '(' || lookahead2.token == TK_STRING || lookahead2.token == '{')) {
                            //修改:和.调用方式 dongxicheng
                            t.token = ':';
                            expdesc key = new expdesc();
                            this.next();
                            this.checkname(key);
                            fs.self(v, key);
                            this.funcargs(v, line);
                        } else {
                            this.fieldsel(v);
                        }
                    }
                    break;
                }
                case '[': { /* `[' exp1 `]' */
                    expdesc key = new expdesc();
                    fs.exp2anyregup(v);
                    this.yindex(key);
                    fs.indexed(v, key);
                    break;
                }
                case ':': { /* `:' NAME funcargs */
                    if (standardSyntax) {//标准语法
                        expdesc key = new expdesc();
                        this.next();
                        this.checkname(key);
                        fs.self(v, key);
                        this.funcargs(v, line);
                    } else {//非标准语法
                        this.fieldsel(v);
                    }
                    break;
                }
                case '(':
                case TK_STRING:
                case '{': { /* funcargs */
                    fs.exp2nextreg(v);
                    this.funcargs(v, line);
                    break;
                }
                default:
                    return;
            }
        }
    }


    void simpleexp(expdesc v) {
		/*
		 * simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor |
		 * FUNCTION body | primaryexp
		 */
        switch (this.t.token) {
            case TK_NUMBER: {
                v.init(VKNUM, 0);
                v.u.setNval(this.t.seminfo.r);
                break;
            }
            case TK_STRING: {
                this.codestring(v, this.t.seminfo.ts);
                break;
            }
            case TK_NIL: {
                v.init(VNIL, 0);
                break;
            }
            case TK_TRUE: {
                v.init(VTRUE, 0);
                break;
            }
            case TK_FALSE: {
                v.init(VFALSE, 0);
                break;
            }
            case TK_DOTS: { /* vararg */
                FuncState fs = this.fs;
                this.check_condition(fs.f.is_vararg != 0, "cannot use " + LUA_QL("...")
                        + " outside a vararg function");
                v.init(VVARARG, fs.codeABC(Lua.OP_VARARG, 0, 1, 0));
                break;
            }
            case '{': { /* constructor */
                this.constructor(v);
                return;
            }
            case TK_FUNCTION: {
                this.next();
                this.body(v, false, this.linenumber);
                return;
            }
            default: {
                this.suffixedexp(v);
                return;
            }
        }
        this.next();
    }


    int getunopr(int op) {
        switch (op) {
            case TK_NOT:
                return OPR_NOT;
            case '-':
                return OPR_MINUS;
            case '#':
                return OPR_LEN;
            default:
                return OPR_NOUNOPR;
        }
    }


    int getbinopr(int op) {
        switch (op) {
            case '+':
                return OPR_ADD;
            case '-':
                return OPR_SUB;
            case '*':
                return OPR_MUL;
            case '/':
                return OPR_DIV;
            case '%':
                return OPR_MOD;
            case '^':
                return OPR_POW;
            case TK_CONCAT:
                return OPR_CONCAT;
            case TK_NE:
                return OPR_NE;
            case TK_EQ:
                return OPR_EQ;
            case '<':
                return OPR_LT;
            case TK_LE:
                return OPR_LE;
            case '>':
                return OPR_GT;
            case TK_GE:
                return OPR_GE;
            case TK_AND:
                return OPR_AND;
            case TK_OR:
                return OPR_OR;
            default:
                return OPR_NOBINOPR;
        }
    }

    static class Priority {
        byte left; /* left priority for each binary operator */

        byte right; /* right priority */

        public Priority(int i, int j) {
            left = (byte) i;
            right = (byte) j;
        }
    }

    ;

    static Priority[] priority = {  /* ORDER OPR */
            new Priority(6, 6), new Priority(6, 6), new Priority(7, 7), new Priority(7, 7), new Priority(7, 7),  /* `+' `-' `/' `%' */
            new Priority(10, 9), new Priority(5, 4),                 /* power and concat (right associative) */
            new Priority(3, 3), new Priority(3, 3),                  /* equality and inequality */
            new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), new Priority(3, 3),  /* order */
            new Priority(2, 2), new Priority(1, 1)                   /* logical (and/or) */
    };

    static int UNARY_PRIORITY = 8;  /* priority for unary operators */


    /*
    ** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
    ** where `binop' is any binary operator with a priority higher than `limit'
    */
    int subexpr(expdesc v, int limit) {
        int op;
        int uop;
        this.enterlevel();
        uop = getunopr(this.t.token);
        if (uop != OPR_NOUNOPR) {
            int line = linenumber;
            this.next();
            this.subexpr(v, UNARY_PRIORITY);
            fs.prefix(uop, v, line);
        } else
            this.simpleexp(v);
		/* expand while operators have priorities higher than `limit' */
        op = getbinopr(this.t.token);
        while (op != OPR_NOBINOPR && priority[op].left > limit) {
            expdesc v2 = new expdesc();
            int line = linenumber;
            this.next();
            fs.infix(op, v);
			/* read sub-expression with higher priority */
            int nextop = this.subexpr(v2, priority[op].right);
            fs.posfix(op, v, v2, line);
            op = nextop;
        }
        this.leavelevel();
        return op; /* return first untreated operator */
    }

    void expr(expdesc v) {
        this.subexpr(v, 0);
    }

	/* }==================================================================== */



	/*
	** {======================================================================
	** Rules for Statements
	** =======================================================================
	*/


    boolean block_follow(boolean withuntil) {
        switch (t.token) {
            case TK_ELSE:
            case TK_ELSEIF:
            case TK_END:
            case TK_EOS:
                return true;
            case TK_UNTIL:
                return withuntil;
            default:
                return false;
        }
    }


    void block() {
	  /* block -> chunk */
        FuncState fs = this.fs;
        BlockCnt bl = new BlockCnt();
        fs.enterblock(bl, false);
        this.statlist();
        fs.leaveblock();
    }


    /*
    ** structure to chain all variables in the left-hand side of an
    ** assignment
    */
    static class LHS_assign {
        LHS_assign prev;
        /* variable (global, local, upvalue, or indexed) */
        expdesc v = new expdesc();
    }

    ;


    /*
    ** check whether, in an assignment to a local variable, the local variable
    ** is needed in a previous assignment (to a table). If so, save original
    ** local value in a safe place and use this safe copy in the previous
    ** assignment.
    */
    void check_conflict(LHS_assign lh, expdesc v) {
        FuncState fs = this.fs;
        short extra = (short) fs.freereg;  /* eventual position to save local variable */
        boolean conflict = false;
        for (; lh != null; lh = lh.prev) {
            if (lh.v.k == VINDEXED) {
				/* table is the upvalue/local being assigned now? */
                if (lh.v.u.ind_vt == v.k && lh.v.u.ind_t == v.u.info) {
                    conflict = true;
                    lh.v.u.ind_vt = VLOCAL;
                    lh.v.u.ind_t = extra;  /* previous assignment will use safe copy */
                }
				/* index is the local being assigned? (index cannot be upvalue) */
                if (v.k == VLOCAL && lh.v.u.ind_idx == v.u.info) {
                    conflict = true;
                    lh.v.u.ind_idx = extra;  /* previous assignment will use safe copy */
                }
            }
        }
        if (conflict) {
		    /* copy upvalue/local value to a temporary (in position 'extra') */
            int op = (v.k == VLOCAL) ? Lua.OP_MOVE : Lua.OP_GETUPVAL;
            fs.codeABC(op, extra, v.u.info, 0);
            fs.reserveregs(1);
        }
    }


    void assignment(LHS_assign lh, int nvars) {
        expdesc e = new expdesc();
        this.check_condition(VLOCAL <= lh.v.k && lh.v.k <= VINDEXED,
                "syntax error");
        if (this.testnext(',')) {  /* assignment -> `,' primaryexp assignment */
            LHS_assign nv = new LHS_assign();
            nv.prev = lh;
            this.suffixedexp(nv.v);
            if (nv.v.k != VINDEXED)
                this.check_conflict(lh, nv.v);
            this.assignment(nv, nvars + 1);
        } else {  /* assignment . `=' explist1 */
            int nexps;
            this.checknext('=');
            nexps = this.explist(e);
            if (nexps != nvars) {
                this.adjust_assign(nvars, nexps, e);
                if (nexps > nvars)
                    this.fs.freereg -= nexps - nvars;  /* remove extra values */
            } else {
                fs.setoneret(e);  /* close last expression */
                fs.storevar(lh.v, e);
                return;  /* avoid default */
            }
        }
        e.init(VNONRELOC, this.fs.freereg - 1);  /* default assignment */
        fs.storevar(lh.v, e);
    }


    int cond() {
		/* cond -> exp */
        expdesc v = new expdesc();
		/* read condition */
        this.expr(v);
		/* `falses' are all equal here */
        if (v.k == VNIL)
            v.k = VFALSE;
        fs.goiftrue(v);
        return v.f.i;
    }

    void gotostat(int pc) {
        int line = linenumber;
        LuaString label;
        int g;
        if (testnext(TK_GOTO))
            label = str_checkname();
        else {
            next();  /* skip break */
            label = LuaString.valueOf("break");
        }
        g = newlabelentry(dyd.gt = LuaC.grow(dyd.gt, dyd.n_gt + 1), dyd.n_gt++, label, line, pc);
        findlabel(g);  /* close it if label already defined */
    }


    /* skip no-op statements */
    void skipnoopstat() {
        while (t.token == ';' || t.token == TK_DBCOLON)
            statement();
    }


    void labelstat(LuaString label, int line) {
		/* label -> '::' NAME '::' */
        int l;  /* index of new label being created */
        fs.checkrepeated(dyd.label, dyd.n_label, label);  /* check for repeated labels */
        checknext(TK_DBCOLON);  /* skip double colon */
		/* create new entry for this label */
        l = newlabelentry(dyd.label = LuaC.grow(dyd.label, dyd.n_label + 1), dyd.n_label++, label, line, fs.pc);
        skipnoopstat();  /* skip other no-op statements */
        if (block_follow(false)) {  /* label is last no-op statement in the block? */
			/* assume that locals are already out of scope */
            dyd.label[l].nactvar = fs.bl.nactvar;
        }
        findgotos(dyd.label[l]);
    }


    void whilestat(int line) {
		/* whilestat -> WHILE cond DO block END */
        FuncState fs = this.fs;
        int whileinit;
        int condexit;
        BlockCnt bl = new BlockCnt();
        this.next();  /* skip WHILE */
        whileinit = fs.getlabel();
        condexit = this.cond();
        fs.enterblock(bl, true);
        this.checknext(TK_DO);
        this.block();
        fs.patchlist(fs.jump(), whileinit);
        this.check_match(TK_END, TK_WHILE, line);
        fs.leaveblock();
        fs.patchtohere(condexit);  /* false conditions finish the loop */
    }

    void repeatstat(int line) {
		/* repeatstat -> REPEAT block UNTIL cond */
        int condexit;
        FuncState fs = this.fs;
        int repeat_init = fs.getlabel();
        BlockCnt bl1 = new BlockCnt();
        BlockCnt bl2 = new BlockCnt();
        fs.enterblock(bl1, true); /* loop block */
        fs.enterblock(bl2, false); /* scope block */
        this.next(); /* skip REPEAT */
        this.statlist();
        this.check_match(TK_UNTIL, TK_REPEAT, line);
        condexit = this.cond(); /* read condition (inside scope block) */
        if (bl2.upval) { /* upvalues? */
            fs.patchclose(condexit, bl2.nactvar);
        }
        fs.leaveblock(); /* finish scope */
        fs.patchlist(condexit, repeat_init); /* close the loop */
        fs.leaveblock(); /* finish loop */
    }


    int exp1() {
        expdesc e = new expdesc();
        int k;
        this.expr(e);
        k = e.k;
        fs.exp2nextreg(e);
        return k;
    }


    void forbody(int base, int line, int nvars, boolean isnum) {
		/* forbody -> DO block */
        BlockCnt bl = new BlockCnt();
        FuncState fs = this.fs;
        int prep, endfor;
        this.adjustlocalvars(3); /* control variables */
        this.checknext(TK_DO);
        prep = isnum ? fs.codeAsBx(Lua.OP_FORPREP, base, NO_JUMP) : fs.jump();
        fs.enterblock(bl, false); /* scope for declared variables */
        this.adjustlocalvars(nvars);
        fs.reserveregs(nvars);
        this.block();
        fs.leaveblock(); /* end of scope for declared variables */
        fs.patchtohere(prep);
        if (isnum)  /* numeric for? */
            endfor = fs.codeAsBx(Lua.OP_FORLOOP, base, NO_JUMP);
        else {  /* generic for */
            fs.codeABC(Lua.OP_TFORCALL, base, 0, nvars);
            fs.fixline(line);
            endfor = fs.codeAsBx(Lua.OP_TFORLOOP, base + 2, NO_JUMP);
        }
        fs.patchlist(endfor, prep + 1);
        fs.fixline(line);
    }


    void fornum(LuaString varname, int line) {
		/* fornum -> NAME = exp1,exp1[,exp1] forbody */
        FuncState fs = this.fs;
        int base = fs.freereg;
        this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX);
        this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT);
        this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP);
        this.new_localvar(varname);
        this.checknext('=');
        this.exp1(); /* initial value */
        this.checknext(',');
        this.exp1(); /* limit */
        if (this.testnext(','))
            this.exp1(); /* optional step */
        else { /* default step = 1 */
            fs.codeABx(Lua.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1)));
            fs.reserveregs(1);
        }
        this.forbody(base, line, 1, true);
    }


    void forlist(LuaString indexname) {
		/* forlist -> NAME {,NAME} IN explist1 forbody */
        FuncState fs = this.fs;
        expdesc e = new expdesc();
        int nvars = 4;   /* gen, state, control, plus at least one declared var */
        int line;
        int base = fs.freereg;
		/* create control variables */
        this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR);
        this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE);
        this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL);
		/* create declared variables */
        this.new_localvar(indexname);
        while (this.testnext(',')) {
            this.new_localvar(this.str_checkname());
            ++nvars;
        }
        this.checknext(TK_IN);
        line = this.linenumber;
        this.adjust_assign(3, this.explist(e), e);
        fs.checkstack(3); /* extra space to call generator */
        this.forbody(base, line, nvars - 3, false);
    }


    void forstat(int line) {
		/* forstat -> FOR (fornum | forlist) END */
        FuncState fs = this.fs;
        LuaString varname;
        BlockCnt bl = new BlockCnt();
        fs.enterblock(bl, true); /* scope for loop and control variables */
        this.next(); /* skip `for' */
        varname = this.str_checkname(); /* first variable name */
        switch (this.t.token) {
            case '=':
                this.fornum(varname, line);
                break;
            case ',':
            case TK_IN:
                this.forlist(varname);
                break;
            default:
                this.syntaxerror(LUA_QL("=") + " or " + LUA_QL("in") + " expected");
        }
        this.check_match(TK_END, TK_FOR, line);
        fs.leaveblock(); /* loop scope (`break' jumps to this point) */
    }


    void test_then_block(IntPtr escapelist) {
		/* test_then_block -> [IF | ELSEIF] cond THEN block */
        expdesc v = new expdesc();
        BlockCnt bl = new BlockCnt();
        int jf;  /* instruction to skip 'then' code (if condition is false) */
        this.next(); /* skip IF or ELSEIF */
        expr(v);  /* read expression */
        this.checknext(TK_THEN);
        if (t.token == TK_GOTO || t.token == TK_BREAK) {
            fs.goiffalse(v); /* will jump to label if condition is true */
            fs.enterblock(bl, false); /* must enter block before 'goto' */
            gotostat(v.t.i); /* handle goto/break */
            skipnoopstat(); /* skip other no-op statements */
            if (block_follow(false)) { /* 'goto' is the entire block? */
                fs.leaveblock();
                return; /* and that is it */
            } else
				/* must skip over 'then' part if condition is false */
                jf = fs.jump();
        } else { /* regular case (not goto/break) */
            fs.goiftrue(v); /* skip over block if condition is false */
            fs.enterblock(bl, false);
            jf = v.f.i;
        }
        statlist(); /* `then' part */
        fs.leaveblock();
        if (t.token == TK_ELSE || t.token == TK_ELSEIF)
            fs.concat(escapelist, fs.jump()); /* must jump over it */
        fs.patchtohere(jf);
    }


    void ifstat(int line) {
        IntPtr escapelist = new IntPtr(NO_JUMP);  /* exit list for finished parts */
        test_then_block(escapelist);  /* IF cond THEN block */
        while (t.token == TK_ELSEIF)
            test_then_block(escapelist);  /* ELSEIF cond THEN block */
        if (testnext(TK_ELSE))
            block();  /* `else' part */
        check_match(TK_END, TK_IF, line);
        fs.patchtohere(escapelist.i);  /* patch escape list to 'if' end */
    }

    void localfunc() {
        expdesc b = new expdesc();
        FuncState fs = this.fs;
        this.new_localvar(this.str_checkname());
        this.adjustlocalvars(1);
        this.body(b, false, this.linenumber);
		/* debug information will only see the variable after this point! */
        fs.getlocvar(fs.nactvar - 1).startpc = fs.pc;
    }


    void localstat() {
		/* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
        int nvars = 0;
        int nexps;
        expdesc e = new expdesc();
        do {
            this.new_localvar(this.str_checkname());
            ++nvars;
        } while (this.testnext(','));
        if (this.testnext('='))
            nexps = this.explist(e);
        else {
            e.k = VVOID;
            nexps = 0;
        }
        this.adjust_assign(nvars, nexps, e);
        this.adjustlocalvars(nvars);
    }


    boolean funcname(expdesc v) {
		/* funcname -> NAME {field} [`:' NAME] */
        if (standardSyntax) {//TODO . 和 : 兼容
            boolean ismethod = false;
            this.singlevar(v);
            while (this.t.token == '.')
                this.fieldsel(v);
            if (this.t.token == ':') {
                ismethod = true;
                this.fieldsel(v);
            }
            return ismethod;
        } else {
            boolean needself = false;
            this.singlevar(v);
            while (this.t.token == '.') {
                needself = true;
                this.fieldsel(v);
            }
            if (this.t.token == ':') {
                needself = false;
                this.fieldsel(v);
            }
            return needself;
        }
    }


    void funcstat(int line) {
		/* funcstat -> FUNCTION funcname body */
        boolean needself;
        expdesc v = new expdesc();
        expdesc b = new expdesc();
        this.next(); /* skip FUNCTION */
        needself = this.funcname(v);
        this.body(b, needself, line);
        fs.storevar(v, b);
        fs.fixline(line); /* definition `happens' in the first line */
    }


    void exprstat() {
		/* stat -> func | assignment */
        FuncState fs = this.fs;
        LHS_assign v = new LHS_assign();
        this.suffixedexp(v.v);
        if (t.token == '=' || t.token == ',') { /* stat -> assignment ? */
            v.prev = null;
            assignment(v, 1);
        } else {  /* stat -> func */
            check_condition(v.v.k == VCALL, "syntax error");
            LuaC.SETARG_C(fs.getcodePtr(v.v), 1);  /* call statement uses no results */
        }
    }

    void retstat() {
		/* stat -> RETURN explist */
        FuncState fs = this.fs;
        expdesc e = new expdesc();
        int first, nret; /* registers with returned values */
        if (block_follow(true) || this.t.token == ';')
            first = nret = 0; /* return no values */
        else {
            nret = this.explist(e); /* optional return values */
            if (hasmultret(e.k)) {
                fs.setmultret(e);
                if (e.k == VCALL && nret == 1) { /* tail call? */
                    LuaC.SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL);
                    LuaC._assert(Lua.GETARG_A(fs.getcode(e)) == fs.nactvar);
                }
                first = fs.nactvar;
                nret = Lua.LUA_MULTRET; /* return all values */
            } else {
                if (nret == 1) /* only one single value? */
                    first = fs.exp2anyreg(e);
                else {
                    fs.exp2nextreg(e); /* values must go to the `stack' */
                    first = fs.nactvar; /* return all `active' values */
                    LuaC._assert(nret == fs.freereg - first);
                }
            }
        }
        fs.ret(first, nret);
        testnext(';');  /* skip optional semicolon */
    }

    void statement() {
        int line = this.linenumber; /* may be needed for error messages */
        enterlevel();
        switch (this.t.token) {
            case ';': { /* stat -> ';' (empty statement) */
                next(); /* skip ';' */
                break;
            }
            case TK_IF: { /* stat -> ifstat */
                this.ifstat(line);
                break;
            }
            case TK_WHILE: { /* stat -> whilestat */
                this.whilestat(line);
                break;
            }
            case TK_DO: { /* stat -> DO block END */
                this.next(); /* skip DO */
                this.block();
                this.check_match(TK_END, TK_DO, line);
                break;
            }
            case TK_FOR: { /* stat -> forstat */
                this.forstat(line);
                break;
            }
            case TK_REPEAT: { /* stat -> repeatstat */
                this.repeatstat(line);
                break;
            }
            case TK_FUNCTION: {
                this.funcstat(line); /* stat -> funcstat */
                break;
            }
            case TK_LOCAL: { /* stat -> localstat */
                this.next(); /* skip LOCAL */
                if (this.testnext(TK_FUNCTION)) /* local function? */
                    this.localfunc();
                else
                    this.localstat();
                break;
            }
            case TK_DBCOLON: { /* stat -> label */
                next(); /* skip double colon */
                labelstat(str_checkname(), line);
                break;
            }
            case TK_RETURN: { /* stat -> retstat */
                next();  /* skip RETURN */
                this.retstat();
                break;
            }
            case TK_BREAK:
            case TK_GOTO: { /* stat -> breakstat */
                this.gotostat(fs.jump());
                break;
            }
            default: {
                this.exprstat();
                break;
            }
        }
        LuaC._assert(fs.f.maxstacksize >= fs.freereg
                && fs.freereg >= fs.nactvar);
        fs.freereg = fs.nactvar; /* free registers */
        leavelevel();
    }

    void statlist() {
		/* statlist -> { stat [`;'] } */
        while (!block_follow(true)) {
            if (t.token == TK_RETURN) {
                statement();
                return; /* 'return' must be last statement */
            }
            statement();
        }
    }

    /*
    ** compiles the main function, which is a regular vararg function with an
    ** upvalue named LUA_ENV
    */
    public void mainfunc(FuncState funcstate) {
        BlockCnt bl = new BlockCnt();
        open_func(funcstate, bl);
        fs.f.is_vararg = 1;  /* main function is always vararg */
        expdesc v = new expdesc();
        v.init(VLOCAL, 0);  /* create and... */
        fs.newupvalue(envn, v);  /* ...set environment upvalue */
        next();  /* read first token */
        statlist();  /* parse main body */
        check(TK_EOS);
        close_func();
    }
	
	/* }====================================================================== */

}
